package layers.ants;

import java.util.HashSet;

import layers.agents.PackageAgent;
import layers.physical.Crossroad;
import events.antEvents.AntCreatedEvent;
import events.antEvents.AntDiedEvent;
import events.antEvents.MarkCreatedEvent;
import events.antEvents.MarkDestroyedEvent;
import events.antEvents.PheromoneStackCreatedEvent;
import events.antEvents.PheromoneStackDestroyedEvent;
import events.packageEvents.PackageCreatedEvent;
import events.packageEvents.PackagePickedUpEvent;
import framework.events.Event;
import framework.events.EventBroker;
import framework.events.EventListener;
import framework.layer.TickListener;

public class AntLayer implements EventListener, TickListener {

	private HashSet<Ant> ants = new HashSet<Ant>();
	private HashSet<Ant> antsToBeAdded = new HashSet<Ant>();
	private HashSet<PackageAgent> packageAgents = new HashSet<PackageAgent>();
	private HashSet<PackageAgent> packageAgentsToBeAdded = new HashSet<PackageAgent>();
	private HashSet<PheromoneStack> stacks = new HashSet<PheromoneStack>();
	private HashSet<PheromoneStack> stacksToBeAdded = new HashSet<PheromoneStack>();
	private HashSet<Crossroad> crossroads = new HashSet<Crossroad>();
	private HashSet<Crossroad> crossroadsToBeAdded = new HashSet<Crossroad>();
	private HashSet<AntTickListener> toBeUnregistered = new HashSet<AntTickListener>();
	private int ticksLeft;

	private final int TURNOVERCOUNT = 100;

	public AntLayer(){
		EventBroker.getEventBroker().register(this);
	}

	private HashSet<Ant> getAnts() {
		return this.ants;
	}

	private HashSet<Ant> getAntsToBeAdded() {
		return antsToBeAdded;
	}

	private HashSet<PackageAgent> getPackageAgents() {
		return this.packageAgents;
	}

	private HashSet<PackageAgent> getPackageAgentsToBeAdded() {
		return packageAgentsToBeAdded;
	}

	@Override
	public void handleEvent(Event event) {
		if(event instanceof AntCreatedEvent){
			AntCreatedEvent e = (AntCreatedEvent) event;
			this.registerAnt(e.getAnt());
		}else if(event instanceof AntDiedEvent){
			AntDiedEvent e = (AntDiedEvent) event;
			this.unregister(e.getAnt());
		}else if(event instanceof PackageCreatedEvent){
			PackageCreatedEvent e = (PackageCreatedEvent) event;
			this.registerPackageAgent(e.getPackageAgent());
		}else if(event instanceof PackagePickedUpEvent){
			PackagePickedUpEvent e = (PackagePickedUpEvent) event;
			this.unregister(e.getPackageAgent());
		}else if(event instanceof PheromoneStackCreatedEvent){
			PheromoneStackCreatedEvent e = (PheromoneStackCreatedEvent) event;
			this.registerPheromoneStack(e.getStack());
		}else if(event instanceof PheromoneStackDestroyedEvent){
			PheromoneStackDestroyedEvent e = (PheromoneStackDestroyedEvent) event;
			this.unregister(e.getStack());
		}else if(event instanceof MarkCreatedEvent){
			MarkCreatedEvent e = (MarkCreatedEvent) event;
			this.registerCrossroad(e.getCrossroad());
		}else if(event instanceof MarkDestroyedEvent){
			MarkDestroyedEvent e = (MarkDestroyedEvent) event;
			this.unregister(e.getCrossroad());
		}
	}

	private void registerCrossroad(Crossroad crossroad) {
		this.crossroadsToBeAdded.add(crossroad);
	}

	private void registerPheromoneStack(PheromoneStack listener) {
		if(listener == null)
			throw new IllegalArgumentException("Listener cannot be null.");
		this.getStacksToBeAdded().add(listener);
	}

	private HashSet<PheromoneStack> getStacksToBeAdded() {
		return this.stacksToBeAdded;
	}

	private HashSet<PheromoneStack> getStacks() {
		return this.stacks;
	}

	private void processAntTick() {
		removeListeners();
		addListeners();
		System.out.println("Stacks: " + this.getStacks().size());
		for(PheromoneStack stack : this.getStacks()){
			stack.processAntTick();
		}
//		System.out.println("Crossroad: " + this.crossroads.size());
		for(Crossroad crossroad : this.crossroads){
			crossroad.processAntTick();
		}
		System.out.println("Ants: " + this.getAnts().size());
		for(Ant listener : this.getAnts()){
			listener.processAntTick();
		}
		for(PackageAgent listener : this.getPackageAgents()){
			listener.processAntTick();
		}
//		System.out.println("Listeners: " + (this.getStacks().size()+this.crossroads.size()+this.getAnts().size()+this.packageAgents.size()));
	}

	private void removeListeners() {
		for(AntTickListener listener : this.getToBeUnregistered()){
			if (listener instanceof Ant)
				this.getAnts().remove(listener);

			if (listener instanceof PackageAgent)
				this.getPackageAgents().remove(listener);

			if (listener instanceof PheromoneStack)
				this.getStacks().remove(listener);

			if (listener instanceof Crossroad)
				this.crossroads.remove(listener);
		}
		this.getToBeUnregistered().clear();
	}
	private void addListeners() {
		this.getAnts().addAll(this.getAntsToBeAdded());
		this.getAntsToBeAdded().clear();
		this.getPackageAgents().addAll(this.getPackageAgentsToBeAdded());
		this.getPackageAgentsToBeAdded().clear();
		this.getStacks().addAll(this.getStacksToBeAdded());
		this.getStacksToBeAdded().clear();
		this.crossroads.addAll(this.crossroadsToBeAdded);
		this.crossroadsToBeAdded.clear();
	}

	@Override
	public void processTick(long timePassed) {
		if (ticksLeft == 0) {
			processAntTick();
			ticksLeft = TURNOVERCOUNT;
		}
		ticksLeft--;
	}

	private void registerAnt(Ant listener){
		if(listener == null)
			throw new IllegalArgumentException("Listener cannot be null.");
		this.getAntsToBeAdded().add(listener);
	}

	private void registerPackageAgent(PackageAgent listener){
		if(listener == null){
			throw new IllegalArgumentException("Listener cannot be null.");
		}
		this.getPackageAgentsToBeAdded().add(listener);
	}

	@Override
	public void terminate() {
		//NOOP?
	}

	private void unregister(AntTickListener listener){
		if(listener == null)
			throw new IllegalArgumentException("Listener cannot be null.");
		this.getToBeUnregistered().add(listener);
	}
	
	private HashSet<AntTickListener> getToBeUnregistered() {
		return this.toBeUnregistered;
	}
}
